﻿using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using Internal.KuiHuaBaoDian.Services.Lockstep;
using Internal.Runtime.Models;
using Internal.Runtime.Services.BattleSkill;
using KuiHuaBaoDian.Services.Lockstep;
using Runtime.Models;
using Runtime.Services.Battle;
using Runtime.Services.Battle.Entity;
using Runtime.Services.BattleAttack;
using Runtime.Services.BattleRecord;
using UnityEngine;
using UnityEngine.Pool;

namespace Runtime.Services.BattleSkill {

    /// <summary>
    /// 战斗技能服务
    /// </summary>
    public partial interface IDFR_BattleSkillServiceTD : IDFR_BattleSkillServiceBase {

        /// <summary>
        /// 为进攻出生时部署技能效果
        /// </summary>
        /// <param name="sourceEntityInstanceID">进攻方实例ID</param>
        void Summon(DFR_BattleEntityInstanceID sourceEntityInstanceID);

        /// <summary>
        /// 在进攻方解除时移除技能效果
        /// </summary>
        /// <param name="sourceEntityInstanceID">进攻方实例ID</param>
        void Dismiss(DFR_BattleEntityInstanceID sourceEntityInstanceID);

        /// <summary>
        /// 给目标添加技能影响效果
        /// </summary>
        /// <param name="targetEntityInstanceID">目标实例ID</param>
        /// <param name="buffProvider">Buff</param>
        void AddBuff(DFR_BattleEntityInstanceID targetEntityInstanceID, IDFR_BattleBuffProvider buffProvider);

        /// <summary>
        /// 测试光环碰撞
        /// </summary>
        /// <param name="targetEntityInstanceID">目标实例ID</param>
        /// <param name="enterSet">碰撞到的光环返回列表</param>
        void TestHalo(DFR_BattleEntityInstanceID targetEntityInstanceID, HashSet<DFR_BattleEntityInstanceID> enterSet);

        /// <summary>
        /// 刷新Buff
        /// </summary>
        /// <param name="targetEntityInstanceID">目标实例ID</param>
        /// <param name="enterSet">碰撞到的光环返回列表</param>
        void RefreshBuff(DFR_BattleEntityInstanceID targetEntityInstanceID, HashSet<DFR_BattleEntityInstanceID> enterSet);

        /// <summary>
        /// 触发效果
        /// </summary>
        /// <param name="effectModel">效果</param>
        /// <param name="skillModel">技能</param>
        /// <param name="lookout">索敌</param>
        /// <param name="majorTarget">主要目标</param>
        bool TriggerEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleEffectLookout lookout, IDFR_BattleAttackTarget majorTarget = null);

        /// <summary>
        /// 部署效果
        /// </summary>
        /// <param name="skillModel">技能数据</param>
        /// <param name="effectModel">效果数据</param>
        /// <param name="lookout">瞭望员，一般生成的技能值为 <c>null</c></param>
        /// <param name="mapPosition">位置</param>
        /// <param name="duration">持续时间</param>
        void DeployEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleAttackLookout lookout, DFR_BattleMapPosition mapPosition, F64 duration);

        /// <summary>
        /// 撤销效果
        /// </summary>
        /// <param name="skillModel">技能数据</param>
        /// <param name="effectModel">效果数据</param>
        void WithdrawEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel);
    }

    /// <summary>
    /// 战斗技能服务
    /// </summary>
    public static class BattleSkillServiceTD {

        /// <summary>
        /// 单例
        /// </summary>
        public static IDFR_BattleSkillServiceTD Instance => m_Instance ??= (IDFR_BattleSkillServiceTD)BattleSkillServiceBase.Instance;
        private static IDFR_BattleSkillServiceTD m_Instance;
    }

    public abstract partial class DFR_BattleSkillServiceTD : DFR_BattleSkillServiceBase, IDFR_BattleSkillServiceTD, IDFR_BattleSkillServiceTDInternal {

        private class LockstepEntity : ILockstepEntity, IDisposable {

            public delegate void LogicFrameDelegate();
            public event LogicFrameDelegate LogicFrame;

            public LockstepEntityHandle Handle;

            public void DoLogicFrame(int frameIndex) {
                LogicFrame?.Invoke();
            }

            public void DoUpdate(int frameIndex, int syncedFrameCount, float deltaTime, float timeScale) {
            }

            public void OnAttached(int frameIndex) {
            }

            public void OnDetached() {
            }

            public void Dispose() {
                LogicFrame = null;
            }
        }

        private class GeneratedSkillRecrod : IDisposable {

            private HashSet<IDFR_BattleEffectModelTD> m_TargetEffectModelSet = new();

            public IEnumerable<IDFR_BattleEffectModelTD> TargetEffectModels => m_TargetEffectModelSet;

            public GeneratedSkillRecrod(IDFR_BattleSkillModelTD skillModel) {
                SkillModel = skillModel;
            }

            public int EffectCount => m_TargetEffectModelSet.Count;

            public void AddTargetEffectModel(IDFR_BattleEffectModelTD effectModel) => m_TargetEffectModelSet.Add(effectModel);

            public void RemoveTargetEffectModel(IDFR_BattleEffectModelTD effectModel) {
                m_TargetEffectModelSet.Remove(effectModel);
            }

            public IDFR_BattleSkillModelTD SkillModel { get; private set; }

            public void Dispose() {
                m_TargetEffectModelSet = null;
                if (SkillModel is IDisposable disposable) {
                    disposable.Dispose();
                }
                SkillModel = null;
            }
        }

        private readonly struct KillCount {
            public int Value { get; init; }
        }

        private readonly struct SkillInstanceID {

            public DFR_BattleEntityInstanceID OwnerInstanceID { get; init; }

            public DFR_BattleEntityID EntityID { get; init; }

            public override bool Equals(object obj) {
                if (obj is SkillInstanceID other) {
                    return other.OwnerInstanceID == OwnerInstanceID && other.EntityID == EntityID;
                }
                return false;
            }

            public override int GetHashCode() => HashCode.Combine(OwnerInstanceID, EntityID);

            public static bool operator ==(SkillInstanceID left, SkillInstanceID right) => left.Equals(right);
            public static bool operator !=(SkillInstanceID left, SkillInstanceID right) => !left.Equals(right);
        }

        private readonly struct SkillEffectInstanceID {

            public SkillInstanceID SkillInstanceID { get; init; }

            public DFR_BattleEntityInstanceID EffectInstanceID { get; init; }

            public override bool Equals(object obj) {
                if (obj is SkillEffectInstanceID other) {
                    return other.SkillInstanceID == SkillInstanceID && other.EffectInstanceID == EffectInstanceID;
                }
                return false;
            }

            public override int GetHashCode() => HashCode.Combine(SkillInstanceID, EffectInstanceID);
        }

        private readonly IDictionary<DFR_BattleEntityInstanceID, HashSet<DFR_BattleEntityInstanceID>> m_AttackingTargetDictionary = new Dictionary<DFR_BattleEntityInstanceID, HashSet<DFR_BattleEntityInstanceID>>();
        private readonly IDictionary<SkillEffectInstanceID, IDictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider>> m_DebuffHaloBuffInfluenceDictionaryDictionary = new Dictionary<SkillEffectInstanceID, IDictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider>>();
        private readonly IDictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider> m_CountdownBuffInfluenceDictionary = new Dictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider>();
        private readonly IDictionary<DFR_BattleEntityInstanceID, SkillEffectInstanceID> m_SkillEffectInstanceIDDictionary = new Dictionary<DFR_BattleEntityInstanceID, SkillEffectInstanceID>();
        private readonly IDictionary<SkillInstanceID, GeneratedSkillRecrod> m_GenerateSkillRecordDictionary = new Dictionary<SkillInstanceID, GeneratedSkillRecrod>();
        private readonly IDictionary<DFR_BattleEntityInstanceID, DebuffHaloBuff> m_DebuffHaloBuffDictionary = new Dictionary<DFR_BattleEntityInstanceID, DebuffHaloBuff>();
        private readonly IDictionary<DFR_BattleEntityInstanceID, CountdownBuff> m_CountdownBuffDictionary = new Dictionary<DFR_BattleEntityInstanceID, CountdownBuff>();

        private IDFR_ReadOnlyBattleAttackSourceDictionaryModelTD m_AttackSourceModelDictionary;
        private IDFR_ReadOnlyBattleAttackTargetDictionaryModelTD m_AttackTargetModelDictionary;
        private IDFR_ReadOnlyBattleCellDictionaryModelTD m_CellModelDictionary;

        private LockstepEntity m_CountdownLockstepEntity;

        private readonly IDictionary<DFR_BattleEntityInstanceID, KillCount> m_AttackerKillCountDictionary = new Dictionary<DFR_BattleEntityInstanceID, KillCount>();
        IEnumerable<(DFR_BattleEntityInstanceID attackSourceEntityInstanceID, int killCount)> IDFR_BattleSkillServiceTDInternal.GetKillCounts() => m_AttackerKillCountDictionary.Select(kv => (kv.Key, kv.Value.Value));
        void IDFR_BattleSkillServiceTDInternal.SetKillCount(DFR_BattleEntityInstanceID attackSourceEntityInstanceID, int value) => m_AttackerKillCountDictionary[attackSourceEntityInstanceID] = new KillCount { Value = value };


        protected override void OnApplicationQuit() {
            m_DebuffHaloBuffDictionary.Clear();
            m_CountdownBuffDictionary.Clear();
            m_GenerateSkillRecordDictionary.Clear();
            m_AttackerKillCountDictionary.Clear();
            m_SkillEffectInstanceIDDictionary.Clear();
            m_DebuffHaloBuffInfluenceDictionaryDictionary.Clear();
            m_CountdownBuffInfluenceDictionary.Clear();
            m_AttackingTargetDictionary.Clear();
        }

        void IDFR_BattleSkillServiceTDInternal.Initialize() {
            Initialize();
            RegisterEffectEntityType(DFR_BattleEntityType.Effect);
            var battleModel = DFR_BattleTD.DataCenter.Models;
            m_AttackSourceModelDictionary = battleModel.AttackSourceDictionary;
            m_AttackTargetModelDictionary = battleModel.AttackTargetDictionary;
            m_CellModelDictionary = battleModel.CellDictionary;
            var lockstepEntity = new LockstepEntity();
            lockstepEntity.LogicFrame += LockstepEntity_LogicFrame;
            var lockstepEntityHandle = (LockstepService.Instance as ILockstepServiceInternal).AttachEntity(lockstepEntity);
            lockstepEntity.Handle = lockstepEntityHandle;
            m_CountdownLockstepEntity = lockstepEntity;
        }

        private void LockstepEntity_LogicFrame() {
            var keys = m_CountdownBuffDictionary.Keys.OrderBy(kv => kv.Index).ToArray();
            foreach (var key in keys) {
                if (m_CountdownBuffDictionary.TryGetValue(key, out var countdownBuff)) {
                    countdownBuff.OnLogicFrame();
                }
            }
        }

        void IDFR_BattleSkillServiceTDInternal.End() {
            (LockstepService.Instance as ILockstepServiceInternal).DetachEntity(m_CountdownLockstepEntity.Handle);
            m_AttackSourceModelDictionary = null;
            m_AttackTargetModelDictionary = null;
            m_CellModelDictionary = null;
            m_CountdownLockstepEntity.Dispose();
            m_CountdownLockstepEntity = null;

            foreach (var kv in m_DebuffHaloBuffDictionary) {
                if (kv.Value is IDisposable disposable) {
                    disposable.Dispose();
                }
            }
            m_DebuffHaloBuffDictionary.Clear();

            foreach (var kv in m_CountdownBuffDictionary) {
                if (kv.Value is IDisposable disposable) {
                    disposable.Dispose();
                }
            }
            m_CountdownBuffDictionary.Clear();

            foreach (var kv in m_GenerateSkillRecordDictionary) {
                kv.Value.Dispose();
            }
            m_GenerateSkillRecordDictionary.Clear();

            foreach (var kv1 in m_DebuffHaloBuffInfluenceDictionaryDictionary) {
                foreach (var kv2 in kv1.Value) {
                    if (kv2.Value is IDisposable disposable) {
                        disposable.Dispose();
                    }
                }
            }
            m_DebuffHaloBuffInfluenceDictionaryDictionary.Clear();

            foreach (var kv in m_CountdownBuffInfluenceDictionary) {
                if (kv.Value is IDisposable disposable) {
                    disposable.Dispose();
                }
            }
            m_CountdownBuffInfluenceDictionary.Clear();

            m_SkillEffectInstanceIDDictionary.Clear();
            m_AttackerKillCountDictionary.Clear();
            foreach (var value in m_AttackingTargetDictionary.Values) {
                HashSetPool<DFR_BattleEntityInstanceID>.Release(value);
            }
            m_AttackingTargetDictionary.Clear();
            End();
        }

        public bool TriggerEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleEffectLookout lookout, IDFR_BattleAttackTarget majorTarget = null) {
            if (effectModel.Type == DFR_BattleEffectType.GenerateSkillEntity) {
                if (skillModel.Type == DFR_BattleSkillType.SpottingRange || skillModel.Type == DFR_BattleSkillType.Halo) {
                    var targets = lookout.Targets.Where(item => item.IsEnabled).Cast<IDFR_BattleCellModelTD>().Where(item => {
                        return BattleAttackService.Instance.IsPointInRectangle(lookout.MapPosition, lookout.SpottingRange, item.Axis);
                    });
                    targets = GetTriggerTargets(targets, skillModel, effectModel);
                    var newSkillModel = KHBD.TypeMap.Instantiate<IDFR_BattleSkillModelTD>();
                    var newSkillID = new DFR_BattleEntityID { Type = DFR_BattleEntityType.Skill, TableID = effectModel.FirstParameter.Int };
                    (newSkillModel as IDFR_BattleSkillModelTDInternal).Initialize(newSkillID, skillModel.OwnerEntityInstanceID, skillModel.OwnerPlayerID, skillModel.Level);
                    //var monsterModel = m_AttackSourceModelDictionary[skillModel.OwnerEntityInstanceID];
                    //if (monsterModel.TryGetPendingTreasureSkillLevel(new T6.DFR_SkillTableID { Type = T6.DFR_SkillIDType.Standard, Value = skillID }, out var treasureSkillID, out var treasureLevel)) {
                    //    var skillTableData = DFR.DataCenter.Tables.TBGameSkill.Get(treasureSkillID);
                    //    if (skillTableData.Type == SkillType.ChangeSkillEffect) {
                    //        newSkillModel.AsSetter().ClearEffect();
                    //    }
                    //    foreach (var effectTableID in skillTableData.Effect) {
                    //        newSkillModel.AsSetter().AddEffect(effectTableID, treasureLevel);
                    //    }
                    //}
                    var generatedSkillRecord = new GeneratedSkillRecrod(newSkillModel);
                    var skillInstanceID = new SkillInstanceID { OwnerInstanceID = newSkillModel.OwnerEntityInstanceID, EntityID = skillModel.EntityInstanceID.ID };
                    var duration = effectModel.Duration <= F64.Zero ? F64.MaxValue : effectModel.Duration;
                    foreach (var target in targets) {
                        foreach (var newEffectModel in newSkillModel.EffectList) {
                            var skillEffectInstanceID = new SkillEffectInstanceID { SkillInstanceID = skillInstanceID, EffectInstanceID = newEffectModel.EntityInstanceID };
                            var targetEffectModel = KHBD.TypeMap.Instantiate<IDFR_BattleEffectModelTD>();
                            (targetEffectModel as IDFR_BattleEffectModelTDInternal).Initialize(newEffectModel.EntityInstanceID.ID, newSkillModel, newEffectModel.Level);
                            generatedSkillRecord.AddTargetEffectModel(targetEffectModel);
                            m_SkillEffectInstanceIDDictionary.Add(targetEffectModel.EntityInstanceID, skillEffectInstanceID);
                            if (!m_DebuffHaloBuffInfluenceDictionaryDictionary.ContainsKey(skillEffectInstanceID)) {
                                m_DebuffHaloBuffInfluenceDictionaryDictionary.Add(skillEffectInstanceID, new Dictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider>());
                            }
                            DeployEffect(targetEffectModel, newSkillModel, null, target.Axis, duration);
                        }
                    }
                    if (generatedSkillRecord.EffectCount > 0 && skillModel.TriggerRate.Value <= F64.Zero) { // 有触发间隔的技能由效果实体维护生命周期
                        m_GenerateSkillRecordDictionary.Add(skillInstanceID, generatedSkillRecord);
                    } else {
                        generatedSkillRecord.Dispose();
                    }
                    return true;
                }
            } else if (skillModel.Type == DFR_BattleSkillType.Halo) {
                if (effectModel.Type == DFR_BattleEffectType.None) {
                    return false;
                }
                // TriggerEffect中的光环效果已经处于非部署阶段了，只能对可攻击目标有效
                if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                    var targets = lookout.Targets.Where(item => item.IsEnabled).Cast<IDFR_BattleAttackTarget>();
                    if (lookout.SpottingRange.Int < int.MaxValue) {
                        targets = targets.Where(item => BattleAttackService.Instance.IsPointInRectangle(lookout.MapPosition, lookout.SpottingRange, item.MapPosition));
                    }
                    targets = GetTriggerTargets(targets, skillModel, effectModel);
                    foreach (var target in targets) {
                        if (effectModel.Duration > F64.Zero) {
                            AddCountdownDebuff(target.EntityInstanceID, effectModel.EntityInstanceID);
                        } else {
                            throw new NotSupportedException($"不支持没有持续时间的技能效果");
                        }
                    }
                } else {
                    throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                }
                return true;
            } else if (skillModel.Type == DFR_BattleSkillType.Immediate) {
                if (skillModel.TriggerTiming is DFR_BattleSkillTriggerTiming.FinishRound) {
                    if (effectModel.IsPlayer) {
                        TriggerPlayerEffect(skillModel, effectModel);
                    } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Source || effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                        if (!TriggerImmediateEffect(effectModel, skillModel, lookout)) {
                            return false;
                        }
                    } else {
                        throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                    }
                    DispatchTriggered(skillModel, lookout.MapPosition);
                    return true;
                } else if (skillModel.TriggerTiming is DFR_BattleSkillTriggerTiming.Hit) {
                    if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                        if (!TriggerImmediateEffect(effectModel, skillModel, lookout, majorTarget)) {
                            return false;
                        }
                    } else {
                        throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                    }
                    DispatchTriggered(skillModel, lookout.MapPosition);
                    return true;
                } else if (skillModel.TriggerTiming is DFR_BattleSkillTriggerTiming.Lord) {
                    if (effectModel.TargetType == DFR_BattleEffectTargetType.Source || effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                        if (!TriggerImmediateEffect(effectModel, skillModel, lookout)) {
                            return false;
                        }
                    } else {
                        throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                    }
                    DispatchTriggered(skillModel, lookout.MapPosition);
                    return true;
                } else {
                    throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleSkillTriggerTiming)}={skillModel.TriggerTiming}]");
                }
            }
            return false;
        }

        private bool TriggerImmediateEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleEffectLookout lookout, IDFR_BattleAttackTarget majorTarget = null) {
            if (effectModel.TargetType == DFR_BattleEffectTargetType.Source) {
                var targets = lookout.Targets.Where(item => item.IsEnabled).Cast<IDFR_BattleAttacker>();
                if (lookout.SpottingRange.Int < int.MaxValue) {
                    targets = targets.Where(item => BattleAttackService.Instance.IsPointInRectangle(lookout.MapPosition, lookout.SpottingRange, item.MapPosition));
                }
                targets = GetTriggerTargets(targets, skillModel, effectModel);
                foreach (var target in targets) {
                    if (effectModel.Duration > F64.Zero) {
                        AddCountdownBuff(target.EntityInstanceID, effectModel.EntityInstanceID, ((DFR_BattleCellAxis)target.MapPosition).ToIndex());
                    } else {
                        throw new NotSupportedException($"不支持没有持续时间的技能效果");
                    }
                }
            } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                IEnumerable<IDFR_BattleAttackTarget> targets;
                if (majorTarget != null && effectModel.InfluenceAmount == 1) {
                    targets = PriorityAttackTargets(null, majorTarget);
                } else {
                    if (majorTarget != null) {
                        targets = lookout.Targets.Where(item => item.IsEnabled).Where(item => item != majorTarget).Cast<IDFR_BattleAttackTarget>();
                    } else {
                        targets = lookout.Targets.Where(item => item.IsEnabled).Cast<IDFR_BattleAttackTarget>();
                    }
                    if (lookout.SpottingRange < F64.MaxValue) {
                        targets = targets.Where(item => BattleAttackService.Instance.IsPointInRectangle(lookout.MapPosition, lookout.SpottingRange, item.MapPosition));
                    }
                    targets = GetTriggerTargets(targets, skillModel, effectModel);
                    targets = PriorityAttackTargets(targets, majorTarget);
                }
                foreach (var target in targets) {
                    if (effectModel.Duration > F64.Zero) {
                        AddCountdownDebuff(target.EntityInstanceID, effectModel.EntityInstanceID);
                    } else {
                        if (effectModel.IsForbiddenEliteOrBoss && target.IsEliteOrBoss) {
                            return false;
                        }
                        if (m_AttackSourceModelDictionary.TryGetValue(skillModel.OwnerEntityInstanceID, out var sourceModel)) {
                            var skillAttacker = new Attacker(sourceModel.AsAttacker(), effectModel, skillModel, null, false);
                            var effectSnapshot = KHBD.TypeMap.Instantiate<IDFR_BattleAttackSnapshot>();
                            effectSnapshot.Initialize(skillAttacker, sourceModel.AsAttacker(), true, effectModel.IsForbiddenEliteOrBoss, 1, null, false);
                            BattleAttackService.Instance.Underattack(effectSnapshot, target, target.EntityInstanceID);
                            (skillAttacker as IDisposable).Dispose();
                            var effectHolder = effectModel.AsHolder();
                            if (effectHolder.HasModelAsset) {
                                BattleServiceBase.Instance.AttachHolder(effectHolder, target.MapPosition, IDFR_BattleSkillServiceBase.BATTLE_HOLDER_AUTO_KILL_TIME);
                            }
                        } else {
                            return false;
                        }
                    }
                }
            } else {
                throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
            }
            return true;
        }

        private IEnumerable<IDFR_BattleAttackTarget> PriorityAttackTargets(IEnumerable<IDFR_BattleAttackTarget> source, params IDFR_BattleAttackTarget[] priority) => CombineAttackTargets(source, priority, null);
        private IEnumerable<IDFR_BattleAttackTarget> CombineAttackTargets(IEnumerable<IDFR_BattleAttackTarget> source, IDFR_BattleAttackTarget[] before, IDFR_BattleAttackTarget[] after) {
            if (before != null) {
                foreach (var item in before) {
                    if (item != null) {
                        yield return item;
                    }
                }
            }
            if (source != null) {
                foreach (var item in source) {
                    if (item != null) {
                        yield return item;
                    }
                }
            }
            if (after != null) {
                foreach (var item in after) {
                    if (item != null) {
                        yield return item;
                    }
                }
            }
        }

        protected abstract void TriggerPlayerEffect(IDFR_BattleSkillModelTD skillModel, IDFR_BattleEffectModelTD effectModel);

        public override void TriggerAttackLaunchSkill(IDFR_BattleSkillModelBase skillModelBase, IDFR_BattleAttacker attacker, IEnumerable<IDFR_BattleAttackTarget> selectedAttackTargets, Action<IDFR_BattleSkillAttacker> onTrigger) {

            UnityEngine.Assertions.Assert.IsNotNull(onTrigger);

            if (skillModelBase is not IDFR_BattleSkillModelTD skillModel) {
                throw new NotSupportedException();
            }

            if (skillModel.TriggerTiming is not DFR_BattleSkillTriggerTiming.Attack) {
                return;
            }

            if (skillModel.Type == DFR_BattleSkillType.Immediate && IsTrigger(skillModel)) {
                for (var i = 0; i < skillModel.EffectList.Count; ++i) {
                    var effectModel = skillModel.EffectList[i];
                    if (effectModel.TargetType == DFR_BattleEffectTargetType.Source) {
                        if (effectModel.Type == DFR_BattleEffectType.InfluenceAmount) {
                            var skillAttacker = new Attacker(attacker, effectModel, skillModel, null, true) {
                                IsCancelNormalAttack = true,
                            };
                            onTrigger.Invoke(skillAttacker);
                        } else {
                            TriggerTarget(effectModel, skillModel, attacker.EntityInstanceID, selectedAttackTargets.FirstOrDefault());
                        }
                    } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                        Dictionary<DFR_BattleEntityInstanceID, IDFR_BattleAttackTarget> killTargetDictionary = null;
                        if (effectModel.Type == DFR_BattleEffectType.Kill) {
                            var threshold = F64.Ratio100(effectModel.FirstParameter.Int);
                            var targets = selectedAttackTargets.Where(item => item.IsEnabled).Where(item => !item.IsEliteOrBoss).Where(item => item.AsAttackDefender().HP <= item.AsAttackDefender().HPMax * threshold);
                            killTargetDictionary = targets.ToDictionary(item => item.EntityInstanceID, item => item);
                            if (killTargetDictionary.Count == 0) {
                                return;
                            }
                        }
                        var skillAttacker = new Attacker(attacker, effectModel, skillModel, killTargetDictionary, false) {
                            IsCancelNormalAttack = killTargetDictionary != null,
                        };
                        onTrigger.Invoke(skillAttacker);
                    } else {
                        throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                    }
                }
                DispatchTriggered(skillModel, attacker.MapPosition);
            } else if (skillModel.Type == DFR_BattleSkillType.AttackCount) {
                var attackLaunchCount = attacker.LaunchCount.Value;
                if (attackLaunchCount % skillModel.RequireAttackLaunchTimes == 0 && IsTrigger(skillModel)) {
                    for (var i = 0; i < skillModel.EffectList.Count; ++i) {
                        var effectModel = skillModel.EffectList[i];
                        var skillAttacker = new Attacker(attacker, effectModel, skillModel, null, false) {
                            IsCancelNormalAttack = true,
                        };
                        if (effectModel.Type == DFR_BattleEffectType.DownTime
                            || effectModel.Type is DFR_BattleEffectType.MoveSpeedFactor or DFR_BattleEffectType.MoveSpeed
                            || effectModel.Type is DFR_BattleEffectType.DefenseFactor or DFR_BattleEffectType.Defense
                            || effectModel.Type is DFR_BattleEffectType.DodgeFactor or DFR_BattleEffectType.Dodge) {
                            skillAttacker.DebuffEffectModel = effectModel;
                        } else {
                            // 正面效果不需处理
                        }
                        onTrigger.Invoke(skillAttacker);
                    }
                    DispatchTriggered(skillModel, attacker.MapPosition);
                }
            } else if (skillModel.Type == DFR_BattleSkillType.SameTarget) {
                var attackerEntityInstanceID = attacker.EntityInstanceID;
                m_AttackingTargetDictionary.TryGetValue(attackerEntityInstanceID, out var attackingTargetInstanceIDSet);
                var newAttackingTargetInstanceIDSet = HashSetPool<DFR_BattleEntityInstanceID>.Get();
                var isAttacking = false;
                foreach (var target in selectedAttackTargets) {
                    newAttackingTargetInstanceIDSet.Add(target.EntityInstanceID);
                    if (!isAttacking && attackingTargetInstanceIDSet != null && attackingTargetInstanceIDSet.Contains(target.EntityInstanceID)) {
                        isAttacking = true;
                    }
                }
                if (isAttacking) {
                    if (IsTrigger(skillModel)) {
                        foreach (var effectModel in skillModel.EffectList) {
                            TriggerTarget(effectModel, skillModel, attackerEntityInstanceID, null);
                        }
                        DispatchTriggered(skillModel, attacker.MapPosition);
                    }
                } else { // 更换目标后移除加成
                    foreach (var effectModel in skillModel.EffectList) {
                        if (m_AttackSourceModelDictionary.TryGetValue(attackerEntityInstanceID, out var attackerModel)) {
                            var buffApplier = attackerModel.AsBuffApplier();
                            buffApplier.RemoveBuff(effectModel.EntityInstanceID.ID, buffProvider => {
                                if (buffProvider is IDFR_BattleEffectHolder effectHolder) {
                                    BattleServiceBase.Instance.DetachHolder(effectHolder.EntityHandle);
                                }
                                buffProvider.Dispose();
                            });
                        }
                    }
                }
                if (attackingTargetInstanceIDSet != null) {
                    HashSetPool<DFR_BattleEntityInstanceID>.Release(attackingTargetInstanceIDSet);
                }
                m_AttackingTargetDictionary[attackerEntityInstanceID] = newAttackingTargetInstanceIDSet;
            }
        }

        public override void TriggerAttackKillSkill(IDFR_BattleSkillModelBase skillModelBase, DFR_BattleEntityInstanceID attackSourceEntityInstanceID, IDFR_BattleAttackTarget attackTarget) {
            if (skillModelBase is not IDFR_BattleSkillModelTD skillModel) {
                throw new NotSupportedException();
            }
            if (skillModel.TriggerTiming is not DFR_BattleSkillTriggerTiming.Kill) {
                return;
            }
            var hasTrigger = false;
            if (skillModel.Type == DFR_BattleSkillType.KillCount) {
                if (!m_AttackerKillCountDictionary.TryGetValue(attackSourceEntityInstanceID, out var killCount)) {
                    m_AttackerKillCountDictionary[attackSourceEntityInstanceID] = killCount = new KillCount { Value = 1 };
                } else {
                    m_AttackerKillCountDictionary[attackSourceEntityInstanceID] = killCount = new KillCount { Value = killCount.Value + 1 };
                }
                if (killCount.Value >= skillModel.RequireKillCount) {
                    m_AttackerKillCountDictionary[attackSourceEntityInstanceID] = new KillCount { Value = 0 };
                    if (IsTrigger(skillModel)) {
                        hasTrigger = true;
                        foreach (var effectModel in skillModel.EffectList) {
                            TriggerTarget(effectModel, skillModel, attackSourceEntityInstanceID, attackTarget);
                        }
                    }
                }
            }
            if (hasTrigger) {
                if (m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                    DispatchTriggered(skillModel, attackSourceModel.AsAttacker().MapPosition);
                }
            }
        }

        public override void TriggerAttackHitSkill(IDFR_BattleSkillModelBase skillModelBase, DFR_BattleEntityInstanceID attackSourceEntityInstanceID, IDFR_BattleAttackTarget attackTarget) {
            if (skillModelBase is not IDFR_BattleSkillModelTD skillModel) {
                throw new NotSupportedException();
            }
            if (skillModel.TriggerTiming is not DFR_BattleSkillTriggerTiming.Hit) {
                return;
            }
            var hasTrigger = false;
            if (skillModel.Type == DFR_BattleSkillType.Immediate) {
                if (IsTrigger(skillModel)) {
                    hasTrigger = true;
                    foreach (var effectModel in skillModel.EffectList) {
                        TriggerTarget(effectModel, skillModel, attackSourceEntityInstanceID, attackTarget);
                    }
                }
            } else if (skillModel.Type == DFR_BattleSkillType.SameTarget) {
                var record = BattleRecordService.Instance.GetAttackSourceInstanceLastNormalAttack(attackSourceEntityInstanceID);
                if (record != null && record.HitTarget.InstanceID == attackTarget.EntityInstanceID) {
                    if (IsTrigger(skillModel)) {
                        hasTrigger = true;
                        foreach (var effectModel in skillModel.EffectList) {
                            TriggerTarget(effectModel, skillModel, attackSourceEntityInstanceID, attackTarget);
                        }
                    }
                } else { // 更换目标后移除加成
                    foreach (var effectModel in skillModel.EffectList) {
                        if (m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                            var buffApplier = attackSourceModel.AsBuffApplier();
                            buffApplier.RemoveBuff(effectModel.EntityInstanceID.ID, buffProvider => {
                                if (buffProvider is IDFR_BattleEffectHolder effectHolder) {
                                    BattleServiceBase.Instance.DetachHolder(effectHolder.EntityHandle);
                                }
                                buffProvider.Dispose();
                            });
                        }
                    }
                }
            }
            if (hasTrigger) {
                if (m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                    DispatchTriggered(skillModel, attackSourceModel.AsAttacker().MapPosition);
                }
            }
        }

        private bool TriggerTarget(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, DFR_BattleEntityInstanceID attackSourceEntityInstanceID, IDFR_BattleAttackTarget target) {
            if (effectModel.TargetType == DFR_BattleEffectTargetType.Source) {
                if (effectModel.RangeType == DFR_BattleEffectRangeType.Self) {
                    //UnityEngine.Assertions.Assert.IsNull(target); // 不需要目标
                    if (m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                        if (effectModel.Duration > F64.Zero) {
                            return AddCountdownBuff(attackSourceEntityInstanceID, effectModel.EntityInstanceID, attackSourceModel.CellIndex);
                        } else {
                            // 融合数据需要不同的HashCode，所以实例化一个包装
                            var buffProvider = KHBD.TypeMap.Instantiate<IDFR_BattleBuffProvider>();
                            var instanceID = new DFR_BattleEntityInstanceID { ID = effectModel.EntityInstanceID.ID };
                            (buffProvider as IDFR_BattleBuffProviderInternal).Initialize(instanceID, isEnableFilter: true, attackSourceModel.EntityInstanceID, effectModel.EntityInstanceID, isEnablePersistence: true);
                            buffProvider.AsSetter().AttachBuff(effectModel.AsBuffProvider());
                            var effectHolder = buffProvider as IDFR_BattleEffectHolder;
                            effectHolder.AsSetter().SetModelAssetKey(effectModel.ModelAssetKey);
                            attackSourceModel.AsBuffApplier().AddBuff(buffProvider);
                            return true;
                        }
                    } else {
                        return false;
                    }
                } else {
                    throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectRangeType)}={effectModel.RangeType}]");
                }
            } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                if (effectModel.Duration > F64.Zero) {
                    return AddCountdownDebuff(target.EntityInstanceID, effectModel.EntityInstanceID);
                } else {
                    if (m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                        var lookout = KHBD.TypeMap.Instantiate<IDFR_BattleEffectLookout>();
                        (lookout as IDFR_BattleEffectLookoutInternal).Initialize(effectModel, skillModel, attackSourceModel.AsAttackLookout(), target.MapPosition);
                        return TriggerEffect(effectModel, skillModel, lookout, target);
                    } else {
                        return false;
                    }
                }
            } else {
                throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
            }
        }

        private IEnumerable<T> GetTriggerTargets<T>(IEnumerable<T> @in, IDFR_BattleSkillModelTD skillModel, IDFR_BattleEffectModelTD effectModel) {
            switch (effectModel.TargetSelectType) {
                case DFR_BattleEffectTargetSelectType.All:
                    foreach (var a in @in) {
                        yield return a;
                    }
                    break;
                case DFR_BattleEffectTargetSelectType.Multiple:
                    var take = effectModel.InfluenceAmount;
                    var multipleList = GetTargetList(@in);
                    for (var i = 0; i < take; ++i) {
                        var multiple = TakeOneTriggerTarget(multipleList);
                        if (multiple == null) {
                            ReleaseTargetList(multipleList);
                            yield break;
                        } else {
                            yield return multiple;
                        }
                    }
                    ReleaseTargetList(multipleList);
                    break;
                case DFR_BattleEffectTargetSelectType.Single:
                    var singleList = GetTargetList(@in);
                    var single = TakeOneTriggerTarget(singleList);
                    if (single == null) {
                        ReleaseTargetList(singleList);
                        yield break;
                    } else {
                        yield return single;
                    }
                    ReleaseTargetList(singleList);
                    break;
                default:
                    throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetSelectType)}={effectModel.TargetSelectType}]");
            }
        }

        private List<T> GetTargetList<T>(IEnumerable<T> @in) {
            var @out = ListPool<T>.Get();
            @out.AddRange(@in);
            return @out;
        }

        private void ReleaseTargetList<T>(List<T> @in) {
            ListPool<T>.Release(@in);
        }

        private T TakeOneTriggerTarget<T>(List<T> list) {
            var count = list.Count;
            T result;
            if (count == 0) {
                result = default;
            } else if (count == 1) {
                result = list[0];
                list.Clear();
            } else {
                var index = BattleServiceBase.Instance.Random(F64.From(count)).Int;
                result = list[index];
                list.RemoveAt(index);
            }
            return result;
        }

        public void Summon(DFR_BattleEntityInstanceID attackSourceEntityInstanceID) {
            if (m_AttackSourceModelDictionary != null && m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                foreach (var skillModel in attackSourceModel.SkillList) {
                    if (skillModel.TriggerTiming == DFR_BattleSkillTriggerTiming.Summon && (skillModel.TriggerProbability.Value >= F64.One || BattleServiceBase.Instance.Random() < skillModel.TriggerProbability.Value)) {
                        foreach (var effectModel in skillModel.EffectList) {
                            DeployEffect(effectModel, skillModel, attackSourceModel.AsAttackLookout(), attackSourceModel.AsSkillMapPositionProvider().MapPosition, F64.Zero);
                        }
                    }
                }
            }
        }

        public void Dismiss(DFR_BattleEntityInstanceID attackSourceEntityInstanceID) {
            if (m_AttackSourceModelDictionary != null && m_AttackSourceModelDictionary.TryGetValue(attackSourceEntityInstanceID, out var attackSourceModel)) {
                foreach (var skillModel in attackSourceModel.SkillList) {
                    foreach (var effectModel in skillModel.EffectList) {
                        WithdrawEffect(effectModel, skillModel);
                    }
                }
            }
        }

        public void DeployEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleAttackLookout lookout, DFR_BattleMapPosition mapPosition, F64 duration) {
            switch (skillModel.Type) {
                case DFR_BattleSkillType.Halo:
                case DFR_BattleSkillType.SpottingRange:
                    AttachHalo(effectModel, skillModel, lookout, mapPosition, duration);
                    break;
                default:
                    break;
            }
        }

        public void WithdrawEffect(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel) {
            switch (skillModel.Type) {
                case DFR_BattleSkillType.Halo:
                case DFR_BattleSkillType.SpottingRange:
                    DetachHalo(effectModel, skillModel);
                    break;
                default:
                    break;
            }
        }

        protected void AttachHalo(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleAttackLookout lookout, DFR_BattleMapPosition mapPosition, F64 duration) {
            var isDebuff = effectModel.TargetType == DFR_BattleEffectTargetType.Target || effectModel.TargetType == DFR_BattleEffectTargetType.Road;
            if (isDebuff) {
                if (skillModel.TriggerRate.Value <= F64.Zero) {
                    if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                        var debuffHaloBuff = new DebuffHaloBuff(mapPosition, effectModel, skillModel);
                        m_DebuffHaloBuffDictionary.Add(effectModel.EntityInstanceID, debuffHaloBuff);
                        if (!m_SkillEffectInstanceIDDictionary.TryGetValue(effectModel.EntityInstanceID, out var _)) {
                            var skillInstanceID = new SkillInstanceID { OwnerInstanceID = skillModel.OwnerEntityInstanceID, EntityID = skillModel.EntityInstanceID.ID };
                            var skillEffectInstanceID = new SkillEffectInstanceID { SkillInstanceID = skillInstanceID, EffectInstanceID = effectModel.EntityInstanceID };
                            m_SkillEffectInstanceIDDictionary.Add(effectModel.EntityInstanceID, skillEffectInstanceID);
                            m_DebuffHaloBuffInfluenceDictionaryDictionary.Add(skillEffectInstanceID, new Dictionary<DFR_BattleEntityInstanceID, IDFR_BattleBuffProvider>());
                        }
                    } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Road) {
                        var effectLookout = KHBD.TypeMap.Instantiate<IDFR_BattleEffectLookout>();
                        (effectLookout as IDFR_BattleEffectLookoutInternal).Initialize(effectModel, skillModel, lookout, mapPosition);
                        TriggerEffect(effectModel, skillModel, effectLookout);
                    } else {
                        throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectTargetType)}={effectModel.TargetType}]");
                    }
                }
                BattleServiceBase.Instance.AttachEffect(effectModel, skillModel, lookout, mapPosition, duration);
            } else {
                var sourceModel = m_AttackSourceModelDictionary[skillModel.OwnerEntityInstanceID];
                var cellIndex = sourceModel.CellIndex;
                AttachPlatformBuff(effectModel, skillModel, lookout, cellIndex);
            }
            DispatchTriggered(skillModel, lookout.MapPosition);
        }

        protected void DetachHalo(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel) {
            var isDebuff = effectModel.TargetType == DFR_BattleEffectTargetType.Target || effectModel.TargetType == DFR_BattleEffectTargetType.Road;
            if (isDebuff) {
                if (effectModel.TargetType == DFR_BattleEffectTargetType.Target) {
                    if (m_DebuffHaloBuffDictionary.TryGetValue(effectModel.EntityInstanceID, out var debuffHaloBuff)) {
                        if (m_SkillEffectInstanceIDDictionary.TryGetValue(effectModel.EntityInstanceID, out var skillEffectInstancID) && m_DebuffHaloBuffInfluenceDictionaryDictionary.TryGetValue(skillEffectInstancID, out var influenceDictionary)) {
                            var keys = influenceDictionary.Keys.ToArray(); // 删除时会修改集合，需要复制Keys
                            foreach (var targetEnityInstanceID in keys) {
                                RemoveBuff(targetEnityInstanceID, effectModel.EntityInstanceID);
                            }
                            m_DebuffHaloBuffInfluenceDictionaryDictionary.Remove(skillEffectInstancID);
                            m_SkillEffectInstanceIDDictionary.Remove(effectModel.EntityInstanceID);
                        }
                        if (debuffHaloBuff is IDisposable disposable) {
                            disposable.Dispose();
                        }
                        m_DebuffHaloBuffDictionary.Remove(effectModel.EntityInstanceID);
                    }
                } else if (effectModel.TargetType == DFR_BattleEffectTargetType.Road) {
                    var skillInstanceID = new SkillInstanceID { OwnerInstanceID = skillModel.OwnerEntityInstanceID, EntityID = skillModel.EntityInstanceID.ID };
                    if (m_GenerateSkillRecordDictionary.TryGetValue(skillInstanceID, out var generateSkillRecord)) {
                        foreach (var targetEffectModel in generateSkillRecord.TargetEffectModels) {
                            WithdrawEffect(targetEffectModel, generateSkillRecord.SkillModel);
                            if (m_SkillEffectInstanceIDDictionary.TryGetValue(targetEffectModel.EntityInstanceID, out var skillEffectInstanceID)) {
                                m_SkillEffectInstanceIDDictionary.Remove(targetEffectModel.EntityInstanceID);
                                if (m_DebuffHaloBuffInfluenceDictionaryDictionary.ContainsKey(skillEffectInstanceID)) {
                                    m_DebuffHaloBuffInfluenceDictionaryDictionary.Remove(skillEffectInstanceID);
                                }
                            }
                        }
                        generateSkillRecord.Dispose();
                    }
                }
                BattleServiceBase.Instance.DetachEffect((effectModel as IDFR_BattleEffectModelTDInternal).EntityHandle);
            } else {
                DetachPlatformBuff(effectModel, skillModel);
            }
        }

        private void AttachPlatformBuff(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel, IDFR_BattleAttackLookout lookout, DFR_BattleCellIndex cellIndex) {
            if (m_CellModelDictionary.ContainsKey(cellIndex)) {
                var cellAxis = cellIndex.ToAxis();
                var gridOption = DFR_BattleTD.DataCenter.Models.GridOption;
                var playerID = skillModel.OwnerPlayerID;
                var rectRange = effectModel.RangeType switch {
                    DFR_BattleEffectRangeType.All => Mathf.Max((int)gridOption.HorizontalCount, (int)gridOption.VerticalCount),
                    DFR_BattleEffectRangeType.Range => effectModel.HaloRange.Int,
                    DFR_BattleEffectRangeType.SpottingRange => lookout.SpottingRange.Int, // 找格子使用整型
                    DFR_BattleEffectRangeType.Self => 0,
                    _ => throw new NotSupportedException($"未支持的参数，如有需要请联系客户端程序 [{nameof(DFR_BattleEffectRangeType)}={effectModel.RangeType}]"),
                };
                var halfH = ((int)gridOption.HorizontalCount - 1) / 2;
                var halfV = ((int)gridOption.VerticalCount - 1) / 2;
                var xStart = Mathf.Clamp(cellAxis.X - rectRange, -halfH, halfH);
                var xEnd = Mathf.Clamp(cellAxis.X + rectRange, -halfH, halfH);
                var yStart = Mathf.Clamp(cellAxis.Y - rectRange, -halfV, halfV);
                var yEnd = Mathf.Clamp(cellAxis.Y + rectRange, -halfV, halfV);
                for (var x = xStart; x <= xEnd; ++x) {
                    for (var y = yStart; y <= yEnd; ++y) {
                        var axis = new DFR_BattleCellAxis { X = x, Y = y };
                        if (m_CellModelDictionary.TryGetValue(axis.ToIndex(), out var cellModel)) {
                            if (cellModel.OwnerPlayerID == playerID) {
                                if (effectModel.Duration > F64.Zero) {
                                    AddCountdownBuff(skillModel.OwnerEntityInstanceID, effectModel.EntityInstanceID, cellModel.Index);
                                } else {
                                    cellModel.AsBuffProviderSetter().AttachBuff(effectModel.AsBuffProvider());
                                }
                            }
                        }
                    }
                }
            }
        }

        private void DetachPlatformBuff(IDFR_BattleEffectModelTD effectModel, IDFR_BattleSkillModelTD skillModel) {
            foreach (var kv in m_CellModelDictionary.OrderBy(kv => kv.Key.Value)) {
                var cellModel = kv.Value;
                cellModel.AsBuffProviderSetter().DetachBuff(effectModel.AsBuffProvider());
            }
        }

        public void TestHalo(DFR_BattleEntityInstanceID attackTargetEntityInstanceID, HashSet<DFR_BattleEntityInstanceID> enterSet) {
            UnityEngine.Assertions.Assert.IsNotNull(enterSet);

            foreach (var kv in m_DebuffHaloBuffDictionary.OrderBy(kv => kv.Key.Index)) {
                var effectInstanceID = kv.Key;
                var debuffHaloBuff = kv.Value;
                if (m_AttackTargetModelDictionary != null && m_AttackTargetModelDictionary.TryGetValue(attackTargetEntityInstanceID, out var targetModel)
                    && targetModel.AsAttackTarget().IsEnabled
                    && BattleAttackService.Instance.IsPointInRectangle(debuffHaloBuff.MapPosition, debuffHaloBuff.Range, targetModel.MapPosition)) {

                    enterSet.Add(kv.Key);
                }
            }
        }

        public void RefreshBuff(DFR_BattleEntityInstanceID attackTargetEntityInstanceID, HashSet<DFR_BattleEntityInstanceID> enterSet) {
            if (m_AttackTargetModelDictionary != null && m_AttackTargetModelDictionary.TryGetValue(attackTargetEntityInstanceID, out var _)) {
                foreach (var effectEntityInstanceID in enterSet) {
                    AddDebuffHaloBuff(attackTargetEntityInstanceID, effectEntityInstanceID);
                }
            }
        }

        private void AddDebuffHaloBuff(DFR_BattleEntityInstanceID attackTargetEntityInstanceID, DFR_BattleEntityInstanceID effectEntityInstanceID) {
            if (m_SkillEffectInstanceIDDictionary.TryGetValue(effectEntityInstanceID, out var skillEffectInstanceID)
                && m_DebuffHaloBuffInfluenceDictionaryDictionary.TryGetValue(skillEffectInstanceID, out var influenceDictionary)
                && !influenceDictionary.ContainsKey(attackTargetEntityInstanceID)
                && m_DebuffHaloBuffDictionary.TryGetValue(effectEntityInstanceID, out var debuffHaloBuff)) {

                var buffProvider = KHBD.TypeMap.Instantiate<IDFR_BattleBuffProvider>();
                (buffProvider as IDFR_BattleBuffProviderInternal).Initialize(debuffHaloBuff.EntityInstanceID, true, debuffHaloBuff.OwnerEntityInstanceID, effectEntityInstanceID);
                buffProvider.AsSetter().AttachBuff(debuffHaloBuff);
                var effectHolder = buffProvider as IDFR_BattleEffectHolder;
                effectHolder.AsSetter().SetModelAssetKey(debuffHaloBuff.ModelAssetKey);
                effectHolder.AsSetter().SyncPositionFrom(attackTargetEntityInstanceID);
                if (IsDOT(buffProvider)) {
                    if (m_AttackTargetModelDictionary.TryGetValue(attackTargetEntityInstanceID, out var attackTargetModel) && attackTargetModel.AsAttackTarget().IsEnabled) {
                        BattleAttackService.Instance.Hit(debuffHaloBuff.Snapshot, attackTargetEntityInstanceID, attackTargetModel.AsAttackTarget(),
                            sourceModelDictionary: m_AttackSourceModelDictionary.AsBase(), targetModelDictionary: m_AttackTargetModelDictionary.AsBase());
                        if (effectHolder.HasModelAsset) {
                            BattleServiceBase.Instance.AttachHolder(effectHolder, attackTargetModel.MapPosition, IDFR_BattleSkillServiceBase.BATTLE_HOLDER_AUTO_KILL_TIME);
                        }
                    }
                } else {
                    AddBuff(attackTargetEntityInstanceID, buffProvider);
                }
                influenceDictionary.Add(attackTargetEntityInstanceID, buffProvider);
            }
        }

        public override bool AddCountdownBuff(DFR_BattleEntityInstanceID sourceEntityInstanceID, DFR_BattleEntityInstanceID effectModelInstanceID, DFR_BattleCellIndex index) {
            if (m_AttackSourceModelDictionary.TryGetValue(sourceEntityInstanceID, out var sourceModel)) {
                if (EffectModelDictionary.TryGetValue(effectModelInstanceID, out var effectModel)) {
                    if (effectModel.Duration > F64.Zero) {
                        var cellModel = DFR_BattleTD.DataCenter.Models.CellDictionary[index];
                        var countdownBuff = new CountdownBuff();
                        countdownBuff.Initialize(effectModel, sourceEntityInstanceID, false);
                        var buffProvider = KHBD.TypeMap.Instantiate<IDFR_BattleBuffProvider>();
                        (buffProvider as IDFR_BattleBuffProviderInternal).Initialize(countdownBuff.EntityInstanceID, true, countdownBuff.OwnerEntityInstanceID, effectModelInstanceID);
                        buffProvider.AsSetter().AttachBuff(countdownBuff);

                        void removeSelf() {
                            if (m_CountdownBuffDictionary.ContainsKey(countdownBuff.EntityInstanceID) && m_CountdownBuffInfluenceDictionary.TryGetValue(countdownBuff.EntityInstanceID, out var countdownBuffProvider)) {
                                cellModel.AsBuffProviderSetter().DetachBuff(countdownBuffProvider);
                                m_CountdownBuffDictionary.Remove(countdownBuff.EntityInstanceID);
                                m_CountdownBuffInfluenceDictionary.Remove(countdownBuff.EntityInstanceID);
                                countdownBuffProvider.Dispose();
                            }
                            if (countdownBuff is IDisposable disposable) {
                                disposable.Dispose();
                            }
                            if (!sourceModel.IsDisposed) {
                                sourceModel.Disposing -= removeSelf;
                            }
                        }
                        countdownBuff.Complete += removeSelf;
                        sourceModel.Disposing += removeSelf;

                        var effectHolder = buffProvider as IDFR_BattleEffectHolder;
                        effectHolder.AsSetter().SetModelAssetKey(countdownBuff.ModelAssetKey);
                        m_CountdownBuffDictionary.Add(countdownBuff.EntityInstanceID, countdownBuff);
                        m_CountdownBuffInfluenceDictionary.Add(countdownBuff.EntityInstanceID, buffProvider);
                        cellModel.AsBuffProviderSetter().AttachBuff(buffProvider);
                        return true;
                    } else {
                        throw new NotSupportedException($"{nameof(AddCountdownBuff)} 不支持没有持续时间的技能效果");
                    }
                }
            }
            return false;
        }

        public override void MoveCountdownBuff(DFR_BattleEntityInstanceID sourceEntityInstanceID, DFR_BattleCellIndex sourceCellIndex, DFR_BattleCellIndex destinationCellIndex) {
            var countdownBuffs = m_CountdownBuffDictionary.Where(kv => kv.Value.OwnerEntityInstanceID == sourceEntityInstanceID).Select(kv => kv.Value);
            foreach (var countdownBuff in countdownBuffs) {
                var countdownBuffProvider = m_CountdownBuffInfluenceDictionary[countdownBuff.EntityInstanceID];
                var sourceCellModel = m_CellModelDictionary[sourceCellIndex];
                sourceCellModel.AsBuffProviderSetter().DetachBuff(countdownBuffProvider);
                var destinationCellModel = m_CellModelDictionary[destinationCellIndex];
                destinationCellModel.AsBuffProviderSetter().AttachBuff(countdownBuffProvider);
            }
            if (m_AttackSourceModelDictionary.TryGetValue(sourceEntityInstanceID, out var sourceModel)) {
                sourceModel.AsBuffApplier().SyncBuffPositionOnce();
            }
        }

        public override void ClearCountdownBuff() {
            foreach (var kv in m_CountdownBuffDictionary) {
                kv.Value.ManualComplete();
            }
        }

        public override bool AddCountdownDebuff(DFR_BattleEntityInstanceID targetEntityInstanceID, DFR_BattleEntityInstanceID effectModelInstanceID) {
            if (EffectModelDictionary.TryGetValue(effectModelInstanceID, out var effectModel)) {
                if (m_AttackTargetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel) && targetModel.AsAttackTarget().IsEnabled) {
                    if (effectModel.Duration > F64.Zero) {
                        var isDOT = IsDOT(effectModel.AsBuffProvider());
                        var countdownBuff = new CountdownBuff();
                        countdownBuff.Initialize(effectModel, targetEntityInstanceID, isDOT);
                        var newBuffProvider = KHBD.TypeMap.Instantiate<IDFR_BattleBuffProvider>();
                        (newBuffProvider as IDFR_BattleBuffProviderInternal).Initialize(countdownBuff.EntityInstanceID, true, countdownBuff.OwnerEntityInstanceID, effectModelInstanceID);
                        newBuffProvider.AsSetter().AttachBuff(countdownBuff);

                        void arrivedChanged() {
                            if (targetModel.IsArrived.Value) {
                                removeSelf();
                            }
                        }
                        void hpChcanged() {
                            if (targetModel.HP.Value <= 0) {
                                removeSelf();
                            }
                        }
                        void removeSelf() {
                            if (isDOT) {
                                m_CountdownBuffDictionary.Remove(countdownBuff.EntityInstanceID);
                                m_CountdownBuffInfluenceDictionary.Remove(countdownBuff.EntityInstanceID);
                                newBuffProvider.Dispose();
                            } else {
                                RemoveBuff(targetModel.EntityInstanceID, countdownBuff.EntityInstanceID);
                            }
                            if (countdownBuff is IDisposable disposable) {
                                disposable.Dispose();
                            }
                            if (!targetModel.IsDisposed) {
                                targetModel.IsArrived.Changed -= arrivedChanged;
                                targetModel.HP.Changed -= hpChcanged;
                            }
                        }
                        countdownBuff.Complete += removeSelf;
                        targetModel.IsArrived.Changed += arrivedChanged;
                        targetModel.HP.Changed += hpChcanged;

                        var effectHolder = newBuffProvider as IDFR_BattleEffectHolder;
                        effectHolder.AsSetter().SetModelAssetKey(countdownBuff.ModelAssetKey);
                        effectHolder.AsSetter().SyncPositionFrom(targetEntityInstanceID);
                        m_CountdownBuffDictionary.Add(countdownBuff.EntityInstanceID, countdownBuff);
                        m_CountdownBuffInfluenceDictionary.Add(countdownBuff.EntityInstanceID, newBuffProvider);

                        if (!isDOT) {
                            AddBuff(targetEntityInstanceID, newBuffProvider);
                        }
                        return true;
                    } else {
                        throw new NotSupportedException($"{nameof(AddCountdownDebuff)} 不支持没有持续时间的技能效果");
                    }
                }
            }
            return false;
        }

        private bool IsDOT(IDFR_BattleBuffProvider buffProvider) {
            var isDOT = false;
            isDOT = isDOT || buffProvider.DamageHPMaxAdditional?.Value > F64.Zero;
            isDOT = isDOT || buffProvider.DamageHPAdditional?.Value > F64.Zero;
            isDOT = isDOT || buffProvider.DamageAdditional?.Value > F64.Zero;
            isDOT = isDOT || buffProvider.DamageFactor?.Value > F64.Zero;
            return isDOT;
        }

        public void AddBuff(DFR_BattleEntityInstanceID targetEntityInstanceID, IDFR_BattleBuffProvider buffProvider) {
            if (m_AttackTargetModelDictionary != null && m_AttackTargetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel) && targetModel.AsAttackTarget().IsEnabled) {
                var buffApplier = targetModel.AsBuffApplier();
                buffApplier.AddBuff(buffProvider);
                if (buffProvider is IDFR_BattleEffectHolder effectHolder && effectHolder.HasModelAsset) {
                    BattleServiceBase.Instance.AttachHolder(effectHolder, targetModel.MapPosition);
                }
            }
        }

        public override void RemoveBuff(DFR_BattleEntityInstanceID targetEntityInstanceID, DFR_BattleEntityInstanceID buffProviderInstanceID) {
            if (m_DebuffHaloBuffDictionary.ContainsKey(buffProviderInstanceID)) {
                if (m_SkillEffectInstanceIDDictionary.TryGetValue(buffProviderInstanceID, out var skillEffectInstanceID) && m_DebuffHaloBuffInfluenceDictionaryDictionary.TryGetValue(skillEffectInstanceID, out var influenceDictionary)) {
                    if (influenceDictionary.TryGetValue(targetEntityInstanceID, out var buffProvider)) {
                        RemoveBuff(targetEntityInstanceID, buffProvider);
                        influenceDictionary.Remove(targetEntityInstanceID);
                        buffProvider.Dispose();
                    }
                }
            }
            if (m_CountdownBuffDictionary.ContainsKey(buffProviderInstanceID) && m_CountdownBuffInfluenceDictionary.TryGetValue(buffProviderInstanceID, out var countdownBuffProvider)) {
                RemoveBuff(targetEntityInstanceID, countdownBuffProvider);
                m_CountdownBuffDictionary.Remove(buffProviderInstanceID);
                m_CountdownBuffInfluenceDictionary.Remove(buffProviderInstanceID);
                countdownBuffProvider.Dispose();
            }
        }
        public override void RemoveBuff(DFR_BattleEntityInstanceID targetEntityInstanceID, IDFR_BattleBuffProvider buffProvider) {
            if (m_AttackTargetModelDictionary != null && m_AttackTargetModelDictionary.TryGetValue(targetEntityInstanceID, out var targetModel)) {
                var buffApplier = targetModel.AsBuffApplier();
                buffApplier.RemoveBuff(buffProvider);
                if (buffProvider is IDFR_BattleEffectHolder effectHolder) {
                    BattleServiceBase.Instance.DetachHolder(effectHolder.EntityHandle);
                }
            }
        }

        public override void ClearBuffInfluence(DFR_BattleEntityInstanceID targetEntityInstanceID) {
            foreach (var skillEffectInstanceID in m_SkillEffectInstanceIDDictionary.Values) {
                if (m_DebuffHaloBuffInfluenceDictionaryDictionary.TryGetValue(skillEffectInstanceID, out var influenceDictionary)) {
                    influenceDictionary.Remove(targetEntityInstanceID);
                }
            }
        }

        void IDFR_BattleSkillServiceBase.AddEffectModel(DFR_BattleEntityInstanceID instanceID, IDFR_BattleEffectModelBase model) {
            AddEffectModel(instanceID, (IDFR_BattleEffectModelTD)model);
        }

        IDFR_BattleEffectModelBase IDFR_BattleSkillServiceBase.GetEffectModel(DFR_BattleEntityInstanceID instanceID) {
            return GetEffectModel(instanceID);
        }
    }
}

namespace Internal.Runtime.Services.BattleSkill {

    public interface IDFR_BattleSkillServiceTDInternal {

        /// <summary>
        /// 需要在LockstepService准备完成后初始化
        /// </summary>
        void Initialize();

        void End();

        void SetKillCount(DFR_BattleEntityInstanceID attackSourceEntityInstanceID, int value);

        IEnumerable<(DFR_BattleEntityInstanceID attackSourceEntityInstanceID, int killCount)> GetKillCounts();
    }
}
